#coding:utf-8

"""
引擎:
    1. 对外提供整个程序的入口
    2. 依次调用其他组件对外提供的接口, 实现整个框架的驱动
"""
from datetime import datetime

from .spider import Spider
from .scheduler import Scheduler
from .downloader import Downloader
from .pipeline import Pipeline
from ..http.item import Item
from ..http.request import Request
from ..middleware.spider_middlewares import SpiderMiddleware
from ..middleware.downloader_middlewares import DownloaderMiddleware
from ..utils.log import logger  # 导入日志系统的日志对象, 这样就只实例化一次, 将日志都记录在一起, 不需要重写new方法实现单例


class Engine(object):
    def __init__(self):
        self.spider = Spider()
        self.scheduler = Scheduler()
        self.downloader = Downloader()
        self.pipeline = Pipeline()

        self.spidermiddleware = SpiderMiddleware()
        self.downloadermiddleware = DownloaderMiddleware()

    def start(self):
        """启动整个引擎"""
        # 加载日志记录
        start = datetime.now()  #起始时间
        logger.info("开始运行时间: %s" % start)  # 使用日志记录起始运行时间
        self._start_engine()
        stop = datetime.now()  # 结束时间
        logger.info("开始运行时间: %s" % stop)  # 使用日志记录结束运行时间
        logger.info("耗时: %.2f" % (stop-start).total_seconds())  # 使用日志记录运行耗时

    def _start_engine(self):
        """依次调用其他组件对外提供的接口, 实现整个框架的驱动"""

        # 1. 爬虫模块发出初始请求
        for request in self.spider.start_request():
            # 利用爬虫中间件预处理请求对象
            request = self.spidermiddleware.process_request(request)
            # 2. 将初始请求添加给调度器
            self.scheduler.add_request(request)

        while True:
            # 3. 从调度器获取请求对象,
            request = self.scheduler.get_request()
            # 若请求队列为空, 则停止爬取
            if request is None:
                break
            # 利用下载中间件对请求进行预处理
            request = self.downloadermiddleware.process_request(request)
            # 4. 利用下载器发起请求
            response = self.downloader.send_request(request)
            # 利用下载中间件对响应进行预处理
            response = self.downloadermiddleware.process_response(response)
            # 5. 利用爬虫的解析响应方法, 处理响应,得到解析后结果
            result = self.spider.parse(response)
            # 6.1 若解析结果是Item数据对象, 则交给管道处理
            if isinstance(result, Item):
                # 利用爬虫中间件预处理Item数据对象
                result = self.spidermiddleware.process_item(result)
                # 管道处理Item数据对象
                self.pipeline.process_item(result)
            # 6.2 若解析结果是请求对象, 则交给调度器处理
            elif isinstance(result, Request):
                # 利用爬虫中间件预处理请求对象
                result = self.spidermiddleware.process_request(result)
                # 调度器处理请求对象
                self.scheduler.add_request(result)
            # 6.3 若解析结果是其他类型, 则抛出异常
            else:
                raise TypeError("Not this Tpye [{}] {}".format(type(result), result))
        
